package com.hdp.kotlin_samples.base

/**
 * author:hdp
 * on:2019/6/12:11:11
 * desc: kotin中的空安全相关
 *
 * Kotlin作为一门高效安全的语言，
 *
 * 1、kotlin为什么引入空安全
 *      1、它尝试在编译阶段就把空指针问题显式的检测出来，把问题留在了编译阶段，让程序更加健壮。
 *      2、引入空安全操作，解决Java中恼人的判空操作，代码可读性更高
 *
 * 1、使用空安全
 *      通过放置一个?来声明可空类型?
 *
 * 2、什么是空安全？
 *      1、当调用一个可空对象的属性时候，如果对象为空，则会返回null
 *      2、当调用一个可空对象的方法的时候，如果对象为空，则不会有操作
 *
 *  important:
 *      1、当对象多层嵌套时候，空安全操作符显示"尤为舒服"
 *         val d=c?.b?.a?.A_name,想想这一行代码在Java里面该咋写
 *
 *      2、给的值是可空类型，需要的参数是非空类型，怎么办？
 *          1）：通过?:操作符指定默认值
 *          2）：通过!!操作符强转为非空（可能抛出Kotlin空指针异常）
 *      3、当Kotlin代码调用Java代码时候，关于空安全，是什么规则呢？
 *          1):当在Java代码中，被@Nullable标记的时候，kotlin会解析成可空类型
 *          2):当在Java代码中，被@NonNull标记的时候，kotlin会解析成非空类型
 *          3):当没有被这两个注解标记的时候，会解析成平台类型：如String会被解析为String!
 *              这时候会被视为非空类型，"建议这时候当做可空类型来处理"
 *
 */


fun main() {
//    test_nullsafe1()
//    test_nullsafe2()
//    test_nullsafe3()
//    test_nullsafe4()
//    test_nullsafe5()
//    test_nullsafe6()
    test_callJavaMethod()
}

private fun test_nullsafe1() {
    var str: String? = "Hello" // 变量被声明为可空
    str = null
    print(str)
    print(str?.length)
}

/**
 * 安全调用在链式调用中很有用
 * 如果任意一个属性（环节）为空，这个链式调用就会返回 null，而不是出现 NPE。
 */
private fun test_nullsafe2() {
    val c: C = C()  //C类的实例对象
    c.b = B()
    c.b?.a = A()
    //获取A_name属性，java语言就需要层层判空来获取
//    if(c!=null&&c.b!=null&&c.b.a!=null){
//        String A_name=c.b.a.A_name;
//    }
    val A_name: String? = c.b?.a?.A_name
    println(A_name)
}

/**
 * 如果只想对非空值执行某个操作，安全调用操作符可以与 let 一起使用：
 */
private fun test_nullsafe3() {
    val listWithNulls: List<String?> = listOf("A", null)
    //只对非空值执行输出
    for (item in listWithNulls) {
        //输出的 it 即为 item
        item?.let { println(it) } // 输出 A 并忽略 null
    }
}

/**
 * Elvis 操作符
 * 当我们有一个可空的引用 b 时，我们可以说 如果 b 非空，我使用它；否则使用某个非空的值 x，
 */
private fun test_nullsafe4() {
    var b: String? = "abcd"
//    b = null
//    val bLength: Int = if (b != null) b.length else -1
    val bLength: Int = b?.length ?: -1
    println(bLength)
}

/**
 *  !! 操作符
 *  !!为 NPE 爱好者准备的。我们可以写 b!! ，表示 如果 b 非空则返回 b，否则就会抛出一个 NPE 异常
 *  这个有时候也会用到，将可空的类型转成不可空的类型
 */
private fun test_nullsafe5() {
//    var b: String? = "abc"
//    b = null
//    val bLength: Int = b!!.length
//    println(bLength)
}

/**
 * 安全的类型转换
 * 我们对一个对象进行强转时，若对象不是目标类型，那么就会导致 ClassCastException。
 * 此时，我们可以使用安全的类型转换，如果尝试转换不成功则返回 null：
 */
private fun test_nullsafe6() {
    val a: String? = "123"
    val aInt: Int? = a as? Int
    println(aInt)
}

//数据类（Entity）
data class A(var A_name: String? = "A_name", var phone: String? = "")

data class B(var B_name: String? = "B_name", var a: A? = null)
data class C(var C_name: String? = "C_name", var b: B? = null)

/**
 * Kotlin调用Java方法
 * 这里要特别注意空安全问题
 */
private fun test_callJavaMethod() {
    val name = tips_7_nullsafe_2.getName()
    println(name?.length)
    val age = tips_7_nullsafe_2.getAge()
    println(age ?: 0 > 18)
}